/**
 * @file types
 * @author  fanxl
 */
import type {ValidateResult} from '../validator/interface';
import type {StringOrNumber, UnionSpecItem, LikeFieldType} from '../interface';

export type SpecDisplay = 'toggle-block' | 'block' | 'none';

/**
 * 目前返回值只有true | string
 * 暂时不存在Promise<true | string>
 * 对jsRule的场景，需要多传一个字段过来parent数据
 */
export type SingleFieldValidator = (value: any, parent?: any) => ValidateResult;
export type FieldValidator = Array<SingleFieldValidator>;
export type UseFieldValidateFn = (value: any, parentValue: any) => Promise<Array<ValidateResult>>;

export type NestedObjectTypeValue<T> = T | NestedObjectType<T> | Array<T | NestedObjectType<T> | NestedObjectType<T>[]>;

export interface NestedObjectType<T> {
    [key: StringOrNumber]: NestedObjectTypeValue<T>;
}

export type ShallowObjectType<T> = Record<string, T>;

export type FieldRegistryType = Record<string, RegisterOptions>;

export type SimpleDataTypeValue = boolean | string | number;

export interface FieldLoadingState {
    isLoading: boolean;
    isBlock: boolean;
    loadingText?: string;
}

export interface RegisterOptions {
    validate: UseFieldValidateFn;
    disabled: boolean;
    readOnly: boolean;
    display: SpecDisplay;
    inlineError: ShallowObjectType<Array<string>>;
    warning: ShallowObjectType<Array<string>>;
    isRemove: boolean;
    isHidden: boolean;
    tip: ShallowObjectType<Array<string>>;
    loadingState: ShallowObjectType<FieldLoadingState>;
    spec: UnionSpecItem;
}

export type ValuesState = NestedObjectType<any>;
export type TouchedState = NestedObjectType<boolean>;
export type ErrorState = ShallowObjectType<Array<string>>;

export interface UseFormProps {
    initialValues?: ValuesState;
    initialPreviewValues?: ValuesState;
    validateOnChange?: boolean;
    validateOnBlur?: boolean;
    options?: ShallowObjectType<any>;
}

export interface FieldSettingOptions {
    shouldValidate?: boolean;
    ifUpdateCharsAboutList?: boolean;
    ifTriggerFormChange?: boolean;
}

export interface UseForm {
    validating: boolean;
    options: ShallowObjectType<any>;

    initialValues: ValuesState;
    values: ValuesState;
    getFieldValue: (fieldName: string) => any;
    setFieldValue: (fieldName: string, value: any, options?: FieldSettingOptions) => void;
    deleteFieldValue: (fieldName: string) => void;

    meshedData: ShallowObjectType<SimpleDataTypeValue | ShallowObjectType<SimpleDataTypeValue>>;

    touched: TouchedState;
    getFieldTouched: (fieldName: string) => NestedObjectTypeValue<boolean>;
    deleteFieldTouched: (fieldName: string) => void;
    setFieldTouched: (fieldName: string, touched: NestedObjectTypeValue<boolean>, shouldValidate?: boolean) => any;
    setFieldsTouched: (touched: TouchedState) => void;

    listChars: ErrorState;
    getListChars: (fieldName: string) => Array<string>;
    setListChars: (fieldName: string, chars: Array<string>) => void;
    deleteListChars: (fieldName: string) => void;
    setBatchListChars: (batchChars: ErrorState) => void;

    errors: ErrorState;
    getFieldError: (fieldName: string) => string[];
    setErrors: (errors: ErrorState) => void;

    registerField: (fieldName: string, registerOptions: RegisterOptions) => void;
    unregisterField: (fieldName: string) => void;

    validateForm: () => Promise<Record<string, Array<string>>>;
    setAllTouched: () => void;
    fieldRegistry: FieldRegistryType;
    getFieldRegistry: (fieldName: string) => RegisterOptions;
    setFieldRegistry: (fieldName: string, field: RegisterOptions) => void;
    setBatchFieldsRegistry: (batchField: FieldRegistryType) => void;
    getInlineState: (path: Array<string>) => Array<string> | FieldLoadingState;
    setInlineState: (path: Array<string>, value: Array<string> | FieldLoadingState) => void;
    setInlineError: (fieldName: string, key: string, error: Array<string>) => void;
    removeInlineError: (fieldName: string, key: string) => void;
    setInlineExpressState: (fieldName: string, type: 'isHidden' | 'isRemove', value: boolean) => void;
    getAllHiddenNodes: () => Array<string>;
    listenFieldsValidate: (fieldOrFields: string | Array<string>, isPrefix?: boolean) => Promise<boolean>;
    globalLoading: boolean;
    $EventBus: any;
}

/**
 * UseField相关类型
 */
export interface FieldConfig {
    name: string;
    path: Readonly<string>;
    validators?: FieldValidator;
    disabled: boolean;
    readOnly: boolean;
    display: SpecDisplay;
    spec: UnionSpecItem;
    remove?: string;
    hidden?: string;
}

export interface FieldInputProps {
    fieldProps: LikeFieldType;
    fieldName: string;
    value: ValuesState;
    chars: Array<string>;
    initialValue: ValuesState;
    errors: Array<string>;
    touched: NestedObjectTypeValue<boolean>;
    formField: RegisterOptions;
    isRemove: boolean;
    isHidden: boolean;
    onChange: (value: any) => void;
    onBlur: () => void;
    /**
     * Attention：
     * 新旧sdk共存期间内，留这个api来抹平两者差异。
     * 后续会删除
     */
    setValue: (value: any) => void;
    setInlineMessage: (messageType: string, extName: string, value: Array<string>) => void;
}

export interface FieldHelperProps {
    setValue: (value: any, shouldValidate?: boolean, ifUpdateCharsAboutList?: boolean) => void;
    setTouched: (value: NestedObjectTypeValue<boolean>, shouldValidate?: boolean) => void;
    setError: (key: string, value: string[]) => void;
    removeError: (key: string) => void;
}

export interface UseField {
    field: FieldInputProps;
    helper: FieldHelperProps;
}
